// --------------------------------------------------------------------------------------------------------------------
// <copyright file="VirtualExecutionOrderPlugin.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.Plugins
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Reflection;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Events;
    using Org.OpenTrader.Framework.Forge.DataCache;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The virtual order execution plugin.
    /// </summary>
    [Credits("Virtual Execution Order Plugin", "Simulate executions", "David Denis & Salah Elmorry", 
        "Copyright (C) David Denis <mailto:david.denis@systemathics.com>")]
    [ImplementsInterface("Org.OpenTrader.Framework.Forge.Interfaces.IOrderPlugin")]
    [ConfigurationField("Slipage", typeof(ConfigurationField.Decimal), "Slipage factor in % 0<= Slipage <=1", IsMandatory = true, DefaultValue = 0.5)]
    [ConfigurationField("AckDelay", typeof(ConfigurationField.Int), "Delay before receiving a market reply (Millisecondes)", IsMandatory = true, 
        DefaultValue = 500)]
    [ConfigurationField("Competition", typeof(ConfigurationField.Bool), "Competition between the different orders", IsMandatory = true, DefaultValue = true)]
    [ConfigurationField("QuoteFactor", typeof(ConfigurationField.Decimal), "Hit ratio : 0<= QuoteFactor <=1", IsMandatory = true, DefaultValue = 0.5)]
    public sealed class VirtualExecutionOrderPlugin : OrderPlugin
    {
        #region Constants and Fields

        /// <summary>
        /// The identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(MethodBase.GetCurrentMethod());

        /// <summary>
        /// Virtual Market, per instrument ID
        /// </summary>
        private IDictionary<Guid, VirtualMarket> virtualMarkets;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="VirtualExecutionOrderPlugin"/> class. 
        /// Initializes a new instance of the <see cref="VirtualOrderExecutionPlugin"/> class.
        /// </summary>
        /// <param name="descriptor">
        /// The descriptor.
        /// </param>
        /// <param name="container">
        /// The container.
        /// </param>
        public VirtualExecutionOrderPlugin(ILiveObjectDescriptor descriptor, ILiveObjectContainer container)
            : base(descriptor, container)
        {
            this.Connection = new VirtualConnection(this);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets ConnectionStatus.
        /// </summary>
        public override EConnectionStatus ConnectionStatus
        {
            get
            {
                return this.Connection.ConnectionStatus;
            }
        }

        /// <summary>
        /// Gets ConnectionURL.
        /// </summary>
        public override string ConnectionURL
        {
            get
            {
                return this.Connection.ToString();
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Cancel the Order
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public override Status Cancel(IOrderWiring wiring, IOrder order, RemotableEventHandler<OrderEventArgs> handler)
        {
            if (!this.virtualMarkets.ContainsKey(wiring.InstrumentID))
            {
                return Status.Failure(Identity, "Cancel: There is no virtual market for this InstrumentID");
            }

            // Cancel to related virtual market
            var cancelstatus = this.virtualMarkets[wiring.InstrumentID].Cancel(order, handler);
            return cancelstatus;
        }

        /// <summary>
        /// The connect.
        /// </summary>
        /// <returns>
        /// </returns>
        public override Status Connect()
        {
            return this.Connection.Connect();
        }

        /// <summary>
        /// The disconnect.
        /// </summary>
        /// <returns>
        /// </returns>
        public override Status Disconnect()
        {
            return this.Connection.Disconnect();
        }

        /// <summary>
        /// Modify the order
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="oldOrder">
        /// The oldOrder.
        /// </param>
        /// <param name="newOrder">
        /// The newOrder.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public override Status Modify(IOrderWiring wiring, IOrder oldOrder, IOrder newOrder, RemotableEventHandler<OrderEventArgs> handler)
        {
            if (!this.virtualMarkets.ContainsKey(wiring.InstrumentID))
            {
                return Status.Failure(Identity, "Modify: There is no virtual market for this InstrumentID");
            }

            // Modify to related virtual market
            var modifytystatus = this.virtualMarkets[wiring.InstrumentID].Modify(oldOrder, newOrder, handler);
            return modifytystatus;
        }

        /// <summary>
        /// On Activation
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="activationEventArgs">
        /// The activation Context.
        /// </param>
        public override void OnActivation(object sender, ActivationEventArgs activationEventArgs)
        {
            if (activationEventArgs is ActivationEventArgs.CreateDestroy)
            {
                switch (activationEventArgs.Type)
                {
                    case EActivationType.Activation:
                        this.virtualMarkets = new Dictionary<Guid, VirtualMarket>();
                        break;

                    case EActivationType.Desactivation:
                        this.virtualMarkets = null;
                        break;
                }
            }
        }

        /// <summary>
        /// Send some order
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public override Status Send(IOrderWiring wiring, IOrder order, RemotableEventHandler<OrderEventArgs> handler)
        {
            // Check type, if not suported type, reject order
            if (order.Type != EOrderType.FillOrKill && order.Type != EOrderType.Limit)
            {
                return Status.Failure(Identity, "Send: Only EOrderType.FillOrKill and EOrderType.Limit are supported");
            }

            // Related instrument
            var instrument = Singleton<DC>.Instance.GetInstrumentStaticDataFromCache(order.InstrumentID);

            // Now, make whatever is needed to have an real-time updated market book to match on
            if (!this.virtualMarkets.ContainsKey(wiring.InstrumentID))
            {
                // The book to receive market updates is created and put to the locally known books map   
                var matcherCell = new VirtualMarket(this, wiring.InstrumentID);

                // Find the Feed Service, it it fails, reject order and remove book from map
                var feedService = Singleton<LiveObjectsRegistry>.Instance.GetLiveObjectParent(instrument.FeedWiring.PluginID) as IFeedService;
                if (feedService == null)
                {
                    return Status.Failure(
                        Identity, "Send: No Feed Service was found so cannot have real time data for this instrument, matching is thus impossible");
                }

                // Put it in the map
                this.virtualMarkets[wiring.InstrumentID] = matcherCell;

                // Ask for real time feed subscription, it it fails, reject order and remove book from map
                if (!feedService.Subscribe(order.InstrumentID, matcherCell.OnFeedEvent))
                {
                    this.virtualMarkets.Remove(wiring.InstrumentID);
                    return Status.Failure(
                        Identity, "Send: Feed Service was found but failed to subscribe to real time data for this instrument, matching is thus impossible");
                }
            }

            // Send to related virtual market
            var sendstatus = this.virtualMarkets[wiring.InstrumentID].Send(order, handler);
            return sendstatus;
        }

        #endregion
    }
}